#!/usr/bin/python

#   Copyright 2010 Matthew Hawn

#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at

#       http://www.apache.org/licenses/LICENSE-2.0

#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.


#need better print functionality
from __future__ import print_function

import re
import sys

#find a common prifix by splitting on '_'
def common_prefix(x, y):
    while x != '':
        if y.startswith(x):
            return x
        x = x.rpartition('_')[0]
    
    return None
    
#compiled regular expressions
klass_pred = re.compile(r'\s*//\s*(?:cl_)?([\w_]+)\s+')
define_pred = re.compile(r'\s*#define\s+CL_([\w_]+)\s+')
end_pred = re.compile(r'\s*//\s*Platform\s+API\s+')
typedef_struct_pred =  re.compile(r'\s*typedef\s+struct.+\s+([\w_]+)\s*;')
typedef_struct_multi_pred = re.compile(r'\s*typedef\s+struct.+{')
typedef_struct_end_pred = re.compile(r'\s*}\s+([\w_]+)\s*;')
typedef_pred = re.compile(r'\s*typedef\s+([\w_]+)\s+([\w_]+)\s*;')
func_begin_pred = re.compile(r'\s*extern\s+CL_API_ENTRY\s+([\w_]+(?:\s+\*)?)\s+CL_API_CALL')
func_name_pred = re.compile(r'\s*([\w_]+)\s*\(\s*(?:const\s+)?([\w_]+(?:\s+\*)?)')
func_param_pred = re.compile(r'\s*(?:const\s+)?([\w_]+(?:\s+\**)?)\s+/\*')
func_f_ptr_pred = re.compile(r'\s*void\s+\(.+\).*,')
func_end_pred = re.compile(r'.+;')

BITFIELD = ('BitfieldList', 'bitfield_list_factory')
INT_CONSTANT = ('IntConstantList', 'int_constant_list_factory')
UINT_CONSTANT = ('UIntConstantList', 'uint_constant_list_factory')
ERROR_CONSTANT = ('ErrorList', 'error_list_factory')

#global state
klass = None
func_args = None
func_name = None
func_return = None
in_struct = False
in_func = False

#result of parsing header
defs={}
structs = []
types = []
funcs={}

with  open(sys.argv[1], "r") as f:
  for line_no, line in enumerate(f):
    try:  
        #set class from preceding comment
        m = klass_pred.match(line)
        if m:
            klass = m.group(1)
            continue
        #defines
        m = define_pred.match(line)
        if m:
            defs.setdefault(klass, list()).append(m.group(1))
            continue
        #structs (mainly the id types)
        m = typedef_struct_pred.match(line)
        if m:
            structs.append(m.group(1))
            continue
        m = typedef_struct_multi_pred.match(line)
        if m:
            in_struct = True
            continue
        if in_struct:
            m = typedef_struct_end_pred.match(line)
            if m:
                in_struct = False
                structs.append(m.group(1))
            continue
        #typedefs
        m = typedef_pred.match(line)
        if m:
            types.append(m.groups())            
            continue
        #function prototypes
        m = func_begin_pred.match(line)
        if m:
            in_func = True
            func_return = m.group(1)
            continue
        if in_func:
            m = func_name_pred.match(line)
            if func_name is None:
                if m:
                    func_name = m.group(1)
                    func_args = [m.group(2)]
                else:
                    print("Warning: Name not found. line: ", line_no)
                    in_func = False
                    continue
            m = func_param_pred.match(line)
            if m:
                func_args.append(m.group(1))
            m = func_f_ptr_pred.match(line)
            if m:
                func_args.append('void *')
            m = func_end_pred.match(line)
            if m:
                in_func = False
                funcs[func_name] = (func_return, func_args)
                func_return = func_args = func_name = None
                continue
    except:
        print(line_no, ":", line)
        raise
        
        
#special cases        
defs_translate = {'Error': 'cl_error',
                'OpenCL': 'cl_version',
                'command': 'command_status'}

for orig, trans in defs_translate.iteritems():

    l = defs[orig]
    del defs[orig]
    defs[trans] = l

structs.remove('cl_image_format')

tab = " " * 4

#for c,i in defs.iteritems():
#    print(c)
#    for x in i:
#        print(tab,x) 

#print() 

#for x in structs:
#    print(x)

#print()

#for x,y in types:
#    print(x,y)

#print()
#for n, f in funcs.iteritems():
#    print(n, f)
    

temp_types = dict((y[3:],x) for x,y in types) #create a dict of type to base mappings(without the 'cl_')

#main output    
with open("cl.pxi",  "w") as out:
    
    
    out.write("""
#   Copyright 2010 Matthew Hawn

#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at

#       http://www.apache.org/licenses/LICENSE-2.0

#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

# Generated from CL/cl.h by generate.py.
""")    
    
    out.write('include "cl_platform.pxi"\n')
    #out.write('__all__  = []\n')
    
    #extern block
    
    out.write('cdef extern from "CL/cl.h" nogil:\n')
    
    #extern structs
    for t in structs:
        out.write(tab + 'ctypedef void_ptr ' + t + '\n')
        
    out.write('\n')
    
    #extern typedefs
    for x,y in types:
        out.write(tab + 'ctypedef ' + x + ' ' + y + '\n')
        
    out.write('\n')
    
    
    out.write('''
    ctypedef struct cl_image_format:
        cl_channel_order        image_channel_order
        cl_channel_type         image_channel_data_type
        \n''')
    
    #extern constants 
      
    for c in sorted(defs.keys()):
        #find type of class
        guess_type = False
        if c in temp_types:
            t = temp_types[c]
        else:
            t = 'cl_int' 
            guess_type = True

        i = defs[c]
        out.write(tab +  "#" + c)
        if guess_type:
            out.write("?")
        out.write('\n')
        for x in i:
            out.write(tab + t + ' CL_' +  x + '\n')
        out.write('\n')
    
    #extern functions
    for n in sorted(funcs.keys()):
        r,args = funcs[n]
        out.write(tab + r + ' ' + n + '(')
        if len(args) == 0 or args[0] == 'void':
            out.write(')\n')
        else:
            out.write('\n')
        
            for a in args[:-1]:
                out.write(tab*2 + a + ',\n')
            out.write(tab*2 + args[-1] + ')\n')

    out.write('\n')

    #output definitions
    

    for c in sorted(defs.keys()):
        i = defs[c]
        
        #find a prefix
        prefix = None
        if len(i) > 1:
            prefix = i[0]
            for x in i[1:]:
                prefix = common_prefix(prefix, x)
                if prefix is None:
                    break
        if prefix is None :
            prefix = c.split('_')[0].upper() 

        #find type of class
        if c in temp_types:
            t = temp_types[c]
        else:
            t = 'cl_int' 
        
        if t == 'cl_bitfield':
            list_def = BITFIELD
        elif t == 'cl_uint':
            list_def = UINT_CONSTANT
        else:
            if c == 'cl_error':
                list_def = ERROR_CONSTANT            
            else:
                list_def = INT_CONSTANT

        out.write("cdef %s c_%s = %s('%s', {\n" %(list_def[0], c, list_def[1], c))
                
        for x in i:
            if x.startswith(prefix):
                y = x[(len(prefix)+1):]
            else:
                y=x
            out.write(tab*2 + '"' + y + '": CL_' + x + ',\n')
        out.write('})\n')
        out.write('%s = c_%s\n' % (c,c))
        #out.write('__all__.append("' + c + '")\n')
        out.write('\n')
     
